#include "UserProfileService_types.h"
#include "UserProfileService.h"

#include <iostream>

#include "melon/service/service_arch.h"
#include "melon/service/base_service_impl.h"

#include "base/logging.h"
#include "base/config_reader.h"
#include "base/get_local_ip.h"

#include "redis_task_manager.h"
#include "update_redis_task.h"

#include "rest_api_adapter.h"
#include "redis_user_profile_adapter.h"

using apache::thrift::concurrency::Runnable;

namespace imserver {

class UserProfileServiceHandler : public UserProfileServiceIf, public melon::service::BaseServiceImpl {
 public:
  UserProfileServiceHandler() {
  }

  virtual void Get(UserProfile& _return, const int64_t qid) {
    for(int i = 0; i < 2; ++i) {
      int ret = RedisUserProfileAdapter::Instance().Get(qid, &_return);
      if (0 == ret) {
        LOG_INFO("Get() from redis ok. qid=" << qid);
        if (qid == 127646892) {
          _return.__set_username("木头的小店");
          _return.__set_shop_name("木头的小店");
          _return.__set_shop_url("http://www.360.cn");
        }
        return;
      } else if (1 == ret) {
        break;
      }
    }

    int rc = RestApiAdapter().Get(qid, &_return);
    if (qid == 127646892) {
      _return.__set_username("木头的小店");
      _return.__set_shop_url("http://www.360.cn");
    }
    LOG_INFO("Get() from REST. qid=" << qid << " rc=" << rc);
    if (_return.qid > 0) {
      RedisTaskManager()->add(boost::shared_ptr<Runnable>(new UpdateRedisTask(_return, 60 * 60 * 48)));
    }
  }

  void GetMissedQids(const UserProfileMap & hited, const std::vector<int64_t> & qids, std::vector<int64_t> * missed_qids) {
    for(size_t i = 0; i < qids.size(); ++i) {
      if (hited.find(qids[i]) == hited.end()) {
        missed_qids->push_back(qids[i]);
      }
    }
  }

  virtual void BatchGet(UserProfileMap & _return, const std::vector<int64_t> & qids) {
    if (qids.empty()) {
      return;
    }
    for(int i = 0; i < 2; ++i) {
      if (0 == RedisUserProfileAdapter::Instance().BatchGet(qids, &_return))
        break;
    }
    size_t redis_hit_size = _return.size();
    
    UserProfileMap rest_get_map;
    std::vector<int64_t> missed_qids;
    bool rest_get_success = false;
    if (_return.size() < qids.size()) {
      GetMissedQids(_return, qids, &missed_qids);
      for(int i = 0; i < 2; ++i) {
        if (0 == RestApiAdapter().BatchGet(missed_qids, &rest_get_map)) { // 注意！是往list后面append, 不能清空list
          rest_get_success = true;
          break;
        }
      }
    }

    LOG_INFO("BatchGet() [0]=" << qids[0] << " redis/rest/total=" << redis_hit_size << '/' << rest_get_map.size() << '/' << qids.size());

    // 从rest中取也失败，则不要写缓存
    if (!rest_get_success) {
      return;
    }

    // 成功从rest取到的数据，要写入到redis
    for(UserProfileMap::const_iterator it = rest_get_map.begin(); it != rest_get_map.end(); ++it) {
      if (it->second.qid > 0) {
        _return.insert(*it);
        LOG_DEBUG("create redis task, qid=" << it->second.qid);
        RedisTaskManager()->add(boost::shared_ptr<Runnable>(new UpdateRedisTask(it->second, 60 * 60 * 48)));
      } else {
        LOG_DEBUG("bad userprofile obj, qid=" << it->second.qid);
      }
    }
    
    // TODO : 在rest中也取不到，依然存放redis, 只是超时时间短一些
    if (rest_get_map.size() < qids.size()) {
      for(size_t i = 0; i < missed_qids.size(); ++i) {
        if (rest_get_map.find(missed_qids[i]) == rest_get_map.end()) {
          LOG_DEBUG("User not found, cache it with smaller TTL. qid=" << missed_qids[i]);
          RedisTaskManager()->add(boost::shared_ptr<Runnable>(new UpdateRedisTask(missed_qids[i], 60 * 60 * 12)));
        }
      }
    }
  }

  virtual void GetByTickets(UserProfile& _return, const std::string& Q, const std::string& T) {
    rest_adapter_.GetByTickets(Q, T, &_return);
    if (_return.qid > 0) {
      LOG_INFO("GetByTickets() ok, qid=" << _return.qid);
    } else {
      LOG_WARN("GetByTickets() err, Q=" << Q << " T=" << T);
    }
  }
 private:
  RestApiAdapter rest_adapter_;
};

}

int main(int argc, char **argv) {
  char * conf_file = "../conf/user_profile_service.conf";
  char opt;
  while ((opt = getopt(argc, argv, "c:")) != -1) {
    switch (opt) {
    case 'c':
      conf_file = optarg;
      break;
    default:
      std::cerr << "Unknown option " << optopt << std::endl;
      return 1;
    }
  }

  ConfigReader cfg(conf_file);
  if (!cfg) {
    std::cerr << "Config file " << conf_file << " read error!" << std::endl;
    return 1;
  }
  LOG_INIT(cfg.Get("log4cplus", "file"), cfg.Get("log4cplus", "level"));

  int port = cfg.GetWithType<int>("user_profile_service", "port", 9092);
  LOG_INFO("listens on port " << port);

  using namespace imserver;
  melon::service::ThriftService<UserProfileServiceHandler, UserProfileServiceProcessor> service;
  service.StartRegister("/im/user_profile_service", "1.0", 0, base::GetLocalIp(), port, 1);
  service.Start(port);
  return 0;
}

